home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 40
/
Aminet 40 (2000)(Schatztruhe)[!][Dec 2000].iso
/
Aminet
/
util
/
boot
/
BlizKick.lha
/
BlizKick
/
bkapi.lha
/
bkapi
/
bkapi.c
next >
Wrap
C/C++ Source or Header
|
2000-03-06
|
22KB
|
889 lines
/* BlizKick EXTRES allocation API functions.
Written by Harry "Piru" Sintonen, Jan 2000.
Public Domain.
*/
#include "bkapi.h"
#define __USE_SYSBASE 1
#include <proto/exec.h>
#include <proto/dos.h>
/****** bkapi.o/--background-- ***********************************************
*
* PURPOSE
* If you don't know what BlizKick is get util/boot/BlizKick.lha
* from aminet and read the documentation of it.
*
* The BlizKick EXTRES buffer is a finite space where BlizKick stores
* resident tags. This area is magically added to Kickstart ROM's
* internal list of memory areas to scan for resident tags. As a
* result BlizKick does not need to use unreliable exec.library
* KickMem & KickPtr vectors.
*
* This package provides easy (as easy as it can get) access to this
* BlizKick EXTRES buffer.
*
* First application to think of, would be ROM Update software that
* could easily kick in new resident module by just calling
* er_allocmem and copying resident tag to memory area returned. On
* next reboot this new resident tag would be activated.
*
* To get things even easier, one can use InternalLoadSeg with
* er_allocmem as allocfunc, er_free as freefunc and dos Read() as
* readfunc. Then just LoadSeg any library / device to make it
* resident. Relocs and such get handled autogically! (however most
* disk based libraries would not survive, namely residenttag ln_Pri
* is wrong.)
*
* Don't expect this API to stay unchanged. There will be some sort
* of MMU protection for EXTRES buffer some day... I think. However
* programs respecting ERH_API_V1 will probably recompile out of the
* box in the future too.
*
* HISTORY
* 1.0.1 - 17th Jan 2000, second release. fixed a typo.
* 1.0.0 - 15th Jan 2000, first release.
*
* LEGAL
* Written by Harry "Piru" Sintonen, Jan 2000.
* bkapi package is public domain.
*
******************************************************************************
*
*/
struct bkerhss *glob_ss = NULL;
/*#define BKDEBUG*/
#ifdef BKDEBUG
# define D(a) (a);
#else
# define D(a) ;
#endif
/****** bkapi.o/er_init ******************************************************
*
* NAME
* er_init -- initialize EXTRES buffer use
*
* SYNOPSIS
* version = er_init()
* D0
*
* ULONG er_init();
*
* FUNCTION
* Initializes use of EXTRES buffer memory pool of BlizKick. This
* routine must be called before using any of the other functions.
*
* RESULT
* version - version number of EXTRES buf API available or zero
* if EXTRES buf could not be found (ie. BlizKick not run).
* Currently ERH_API_V1.
*
* SEE ALSO
*
******************************************************************************
*
*/
ULONG er_init(void) {
struct bkerhss *ss;
if (!glob_ss) {
Forbid();
ss = (struct bkerhss *) FindSemaphore("EXTRES Handler");
Permit();
if (ss) {
switch (ss->version) {
case ERH_API_V1:
glob_ss = ss;
break;
/*
case ERH_API_V2:
glob_ss = ss;
... could do some version dependant check / init here ...
break;
*/
}
/* force sanity check */
er_availmem(MEMF_LARGEST);
}
}
return glob_ss ? glob_ss->version : 0;
}
/****** bkapi.o/er_alloc *****************************************************
*
* NAME
* er_alloc -- allocate bytesize bytes from EXTRES memory pool
*
* SYNOPSIS
* memoryblock = er_alloc(bytesize)
* D0 D0
*
* void *er_alloc(ULONG);
*
* FUNCTION
* Allocates memory from EXTRES buffer memory pool of BlizKick. This
* memory area is specific in a way that resident tags will be scanned
* from this area on system boot up.
*
* This means easy addition of system transparent resident tags.
* System transparent means no kickmem or kicktag pointers will be
* used.
*
* INPUTS
* bytesize - the size of the desired block in bytes. (will
* automatically round this number to a multiple of the
* system memory chunk size)
*
* RESULT
* memoryblock - a pointer to the newly allocated memory block.
* If there are no free memory regions large enough to satisfy
* the request, zero will be returned. The pointer must be
* The memory block returned is quad word aligned.
*
* WARNING
* The result of any memory allocation MUST be checked, and a viable
* error handling path taken. ANY allocation may fail if memory has
* been filled.
*
* NOTE
* Allocation *will* fail if BlizKick is not run.
*
* SEE ALSO
* er_allocmem, er_free, exec.library/Allocate
*
******************************************************************************
*
*/
void * ASM er_alloc(REG(d0,ULONG bytesize)) {
void *ret = NULL;
if (glob_ss) {
ObtainSemaphore(&glob_ss->ss);
ret = Allocate(glob_ss->mh, bytesize);
ReleaseSemaphore(&glob_ss->ss);
}
if (!ret) SetIoErr(ERROR_NO_FREE_STORE);
return ret;
}
/****** bkapi.o/er_allocmem **************************************************
*
* NAME
* er_allocmem -- AllocMem wrapper for er_alloc
*
* SYNOPSIS
* memoryblock = er_allocmem(bytesize, attributes)
* D0 D0 D1
*
* void *er_allocmem(ULONG, ULONG);
*
* FUNCTION
* Allocates memory from EXTRES buffer memory pool of BlizKick. This
* memory area is specific in a way that resident tags will be scanned
* from this area on system boot up.
*
* This means easy addition of system transparent resident tags.
* System transparent means no kickmem or kicktag pointers will be
* used.
*
* This function is quite similar to er_alloc, the only difference
* is that er_allocmem supports MEMF_CLEAR.
*
* If MEMF_CHIP is specified this function will fail.
*
* INPUTS
* bytesize - the size of the desired block in bytes. (will
* automatically round this number to a multiple of the
* system memory chunk size)
*
* attributes -
* requirements
*
* MEMF_CHIP: Will cause the allocation to fail. EXTRES
* buffer memory is in fastmem.
*
* options
*
* MEMF_CLEAR: The memory will be initialized to all
* zeros.
*
* RESULT
* memoryBlock - a pointer to the newly allocated memory block.
* If there are no free memory regions large enough to satisfy
* the request, zero will be returned. The pointer must be
* checked for zero before the memory block may be used!
* The memory block returned is quad word aligned.
*
* WARNING
* The result of any memory allocation MUST be checked, and a viable
* error handling path taken. ANY allocation may fail if memory has
* been filled.
*
* NOTE
* Allocation *will* fail if BlizKick is not run.
*
* MEMF_CLEAR is the only flag supported! Other flags will be
* silentry ignored, except MEMF_CHIP that will cause allocation to
* fail.
*
* This function is provided for completeness and also for use as
* dos.library/InternalLoadSeg() allocfunc.
*
* SEE ALSO
* er_alloc, er_free, exec.library/AllocMem, exec/memory.h
*
******************************************************************************
*
*/
void * __saveds ASM er_allocmem(REG(d0,ULONG bytesize), REG(d1,ULONG attributes)) {
void *ret = NULL;
ULONG *clearptr;
if ( (bytesize==0) || (attributes & MEMF_CHIP) ) return NULL;
if (glob_ss) {
ObtainSemaphore(&glob_ss->ss);
if ( (ret = Allocate(glob_ss->mh, bytesize)) ) {
D(Printf("er_allocmem(%ld) = $%lx\n",
bytesize,(ULONG) ret));
/* actually the following could clear a bit too much, but it
doesn't really matter. should clear only bytesize bytes. */
if (attributes & MEMF_CLEAR) {
clearptr = (ULONG *) ret;
bytesize = (bytesize + MEM_BLOCKMASK) & -MEM_BLOCKSIZE;
D(Printf("er_allocmem: clearing %ld bytes from $%lx\n",
bytesize,(ULONG) clearptr));
for ( ; bytesize; bytesize -= 8) {
*clearptr++ = 0;
*clearptr++ = 0;
}
}
}
ReleaseSemaphore(&glob_ss->ss);
}
if (!ret) SetIoErr(ERROR_NO_FREE_STORE);
return ret;
}
/****** bkapi.o/er_free ******************************************************
*
* NAME
* er_free -- deallocate memory from EXTRES memory pool
*
* SYNOPSIS
* er_free(memoryblock, bytesize)
* A1 D0
*
* void er_free(void *, ULONG);
*
* FUNCTION
* Erase and free a region of memory, returning it to the EXTRES
* memory pool.
*
* INPUTS
* memoryblock - pointer to the memory block to free
* bytesize - the size of the desired block in bytes. (will
* automatically round this number to a multiple of the
* system memory chunk size)
*
* NOTE
* If a block of memory is freed twice, the system will Guru. The
* Alert is AN_FreeTwice ($01000009). If you pass the wrong pointer,
* you will probably see AN_MemCorrupt $01000005.
*
* Will also fill the memory area to be released with all ones
* before releasing. This ensures no partial resident tag will
* remain in unallocated memory.
*
* Both memory allocated by er_alloc and er_allocmem must be released
* with this function!
*
* SEE ALSO
* er_alloc, er_allocmem, exec.library/Allocate
*
******************************************************************************
*
*/
void __saveds ASM er_free(REG(a1,void *memblock), REG(d0,ULONG bytesize)) {
ULONG *fillptr; ULONG fillsize;
if ( (bytesize==NULL) || (memblock==NULL) ) return;
if (glob_ss) {
/* actually the following could fill a bit too much, but it
doesn't really matter. should fill only bytesize bytes. */
fillptr = (ULONG *) ((ULONG)memblock & -MEM_BLOCKSIZE);
fillsize = (bytesize + MEM_BLOCKMASK +
((ULONG) fillptr - (ULONG) memblock)) &
-MEM_BLOCKSIZE;
/* some sanity checking - note that doesn't require ObtainSemaphore
because only mh_Lower and mh_Upper are read (both are static) */
if ( ((ULONG) fillptr < (ULONG) glob_ss->mh->mh_Lower) ||
(((ULONG)fillptr + fillsize) > (ULONG) glob_ss->mh->mh_Upper) ) {
D(Printf("er_free: *** illegal arguments ***!\n"));
return;
}
D(Printf("er_free, memblock: $%lx bytesize: %ld fillptr: $%lx fillsize: %ld\n",
(ULONG) memblock, bytesize, (ULONG) fillptr, fillsize));
ObtainSemaphore(&glob_ss->ss);
for ( ; fillsize; fillsize -= 8) {
*fillptr++ = 0xffffffff;
*fillptr++ = 0xffffffff;
}
Deallocate(glob_ss->mh, memblock, bytesize);
ReleaseSemaphore(&glob_ss->ss);
}
}
/****** bkapi.o/er_allocvec **************************************************
*
* NAME
* er_allocvec -- allocate EXTRES memory and keep track of the size
*
* SYNOPSIS
* memoryblock = er_allocvec(bytesize, attributes)
* D0 D0 D1
*
* void *er_allocvec(ULONG, ULONG);
*
* FUNCTION
* This function works similar to er_allocmem(), but tracks the size
* of the allocation.
*
* See the er_allocmem() documentation for details.
*
* RESULT
* memoryBlock - a pointer to the newly allocated memory block.
* If there are no free memory regions large enough to satisfy
* the request, zero will be returned. The pointer must be
* checked for zero before the memory block may be used!
* The memory block returned is *long* word aligned.
*
* WARNING
* The result of any memory allocation MUST be checked, and a viable
* error handling path taken. ANY allocation may fail if memory has
* been filled.
*
* SEE ALSO
* er_allocmem, er_freevec, exec.library/AllocVec
*
******************************************************************************
*
*/
void * ASM er_allocvec(REG(d0,ULONG bytesize), REG(d1,ULONG attributes)) {
void *ret;
bytesize += 4;
if ( (ret = er_allocmem(bytesize, attributes)) ) {
*(ULONG *)ret = bytesize;
return (void *) ((ULONG) ret + 4);
}
return NULL;
}
/****** bkapi.o/er_freevec ***************************************************
*
* NAME
* er_freevec -- free er_allocvec() EXTRES memory
*
* SYNOPSIS
* er_freevec(memoryblock)
* A1
*
* void er_freevec(void *);
*
* FUNCTION
* Free an allocation made by the er_allocvec() call.
*
* NOTE
* If a block of memory is freed twice, the system will Guru. The
* Alert is AN_FreeTwice ($01000009). If you pass the wrong pointer,
* you will probably see AN_MemCorrupt $01000005.
*
* INPUTS
* memoryblock - pointer to the memory block to free, or NULL.
*
* SEE ALSO
* er_allocvec, exec.library/FreeVec
*
******************************************************************************
*
*/
void ASM er_freevec(REG(a1,void *memoryblock)) {
ULONG size;
if (memoryblock) {
memoryblock = (void *) ((ULONG) memoryblock - 4);
size = *((ULONG *) memoryblock);
er_free( memoryblock, size);
}
}
/****** bkapi.o/er_lock ******************************************************
*
* NAME
* er_lock -- lock EXTRES buffer memory
*
* SYNOPSIS
* er_lock()
*
* void er_lock(void);
*
* FUNCTION
* Locks access to EXTRES buffer memory for this task only.
*
* WARNING
* Be *very* careful not to keep the lock if another process you
* depend/wait will try to er_lock() simultanously!
*
* NOTE
* You *must* er_lock() before you read/write EXTRES buffer memory
* area. Call er_unlock() when done tempering with it.
*
* er_lock() and er_unlock() nest.
*
* SEE ALSO
* er_unlock, er_getarea
*
******************************************************************************
*
*/
void ASM er_lock(void) {
if (glob_ss) {
ObtainSemaphore(&glob_ss->ss);
}
}
/****** bkapi.o/er_unlock ****************************************************
*
* NAME
* er_unlock -- unlock EXTRES buffer memory
*
* SYNOPSIS
* er_unlock()
*
* void er_unlock(void);
*
* FUNCTION
* Unlock access to EXTRES buffer memory. Other tasks may bid for
* access now.
*
* WARNING
* Be *very* careful not to keep the lock if another process you
* depend/wait will try to er_lock() simultanously!
*
* NOTE
* You *must* er_lock() before you read/write EXTRES buffer memory
* area. Call er_unlock() when done tempering with it.
*
* er_lock() and er_unlock() nest.
*
* SEE ALSO
* er_lock, er_getarea
*
******************************************************************************
*
*/
void ASM er_unlock(void) {
if (glob_ss) {
ReleaseSemaphore(&glob_ss->ss);
}
}
/****** bkapi.o/er_getarea ***************************************************
*
* NAME
* er_getarea -- get EXTRES resident module area
*
* SYNOPSIS
* start = er_getarea(len_ptr)
* D0 A0
*
* void *er_getarea(ULONG *);
*
* FUNCTION
* Return lower and upper bound of memory area covered by EXTRES
* buffer.
*
* INPUTS
* len_ptr - pointer to ULONG to put area lenght to.
*
* RESULT
* start - pointer to start of resident module area. If there's no
* BlizKick EXTRES buffer available will be zero.
*
* NOTE
* You *must* er_lock() before you read/write EXTRES buffer memory
* area. Call er_unlock() when done tempering with it.
*
* Will return zero if BlizKick is not run.
*
* SEE ALSO
* er_lock, er_unlock
*
******************************************************************************
*
*/
ULONG ASM er_getarea(REG(a0,ULONG *len_ptr)) {
ULONG ret = NULL;
if (glob_ss) {
ObtainSemaphore(&glob_ss->ss);
ret = (ULONG) glob_ss->mh->mh_Lower;
*len_ptr = (ULONG) glob_ss->mh->mh_Upper - ret;
ReleaseSemaphore(&glob_ss->ss);
}
return ret;
}
/****** bkapi.o/er_availmem **************************************************
*
* NAME
* er_availmem -- return EXTRES buffer memory available
*
* SYNOPSIS
* size = er_availmem(requirements)
* D0 D1
*
* ULONG er_availmem(ULONG);
*
* FUNCTION
* This function returns the amount of free EXTRES buffer memory.
*
* To find out what the largest block is, specify MEMF_LARGEST in
* attributes argument.
*
* WARNING
* Due to the effect of multitasking, the value returned may not
* actually be the amount of free memory available at that instant.
* However if you have locked memory with er_lock() before, the
* result is exact.
*
* INPUTS
* requirements - MEMF_LARGEST results calculation of the size of
* the largest block.
*
* RESULT
* size - total free space remaining (or the largest free block).
*
* NOTE
* er_availmem(MEMF_LARGEST) does a consistency check on the
* memory list. In case of an error 0xFFFFFFFF is returned, and
* further allocations are impossible. If memory header itself is
* bad will alert with BKA_MemoryInsane.
*
* SEE ALSO
* exec.library/AvailMem, exec/memory.h
*
******************************************************************************
*
*/
ULONG ASM er_availmem(REG(d1,ULONG attributes)) {
struct MemHeader *mh;
struct MemChunk *mc, *predmc;
ULONG ret = NULL, free = 0, failed = 0;
if (glob_ss) {
ObtainSemaphore(&glob_ss->ss);
if (attributes & MEMF_LARGEST) {
mh = glob_ss->mh;
if ( (mh->mh_Node.ln_Succ == NULL) &&
(mh->mh_Node.ln_Pred == NULL) &&
(mh->mh_Node.ln_Type == NT_MEMORY) &&
(((ULONG) mh->mh_Lower & MEM_BLOCKMASK) == 0) &&
(((ULONG) mh->mh_Upper & MEM_BLOCKMASK) == 0) &&
(mh->mh_Lower < mh->mh_Upper)
) {
mc = mh->mh_First;
predmc = (struct MemChunk *) ((ULONG) mc - 1);
while (mc && (!failed) ) {
if ( ( ((ULONG) mc & MEM_BLOCKMASK) == 0) &&
((ULONG) mc > (ULONG) predmc) &&
((ULONG) mc <= ((ULONG) mh->mh_Upper - MEM_BLOCKSIZE))
) {
if ( (mc->mc_Bytes & MEM_BLOCKMASK) == 0) {
free += mc->mc_Bytes;
if (mc->mc_Bytes > ret) ret = mc->mc_Bytes;
} else failed = 1;
predmc = mc;
mc = mc->mc_Next;
} else failed = 1;
}
} else {
failed = 2;
}
if ( (!failed) && (free != mh->mh_Free) ) failed = 1;
switch (failed) {
case 2:
/* memheader screwed up!! */
D(Printf("er_availmem(largest): *** memheader fucked ***!!\n"));
Alert(BKA_MemoryInsane);
/* fall thru */
case 1:
D(Printf("er_availmem(largest): freelist fucked!\n"));
mh->mh_First = NULL;
mh->mh_Free = 0;
ret = 0xFFFFFFFF;
break;
}
} else {
ret = glob_ss->mh->mh_Free;
}
ReleaseSemaphore(&glob_ss->ss);
}
return ret;
}
/****** bkapi.o/er_nextresident **********************************************
*
* NAME
* er_nextresident -- find first/next resident tag from EXTRES buffer
*
* SYNOPSIS
* nextresident = er_nextresident(oldresident)
* D0 A0
*
* struct Resident *er_nextresident(struct Resident *);
*
* FUNCTION
* Find first or next Resident tag from EXTRES buffer memory.
*
* WARNING
* You *must* have er_lock() on memory before calling this routine!
* Keep the lock until you're done accessing the resident tag(s).
*
* INPUTS
* oldresident - startpoint for search, will not find this
* particular resident but next. Pass NULL to find first
* resident.
*
* RESULT
* nextresident - pointer to resident tag structure or zero if no
* more resident tags could be found.
*
* EXAMPLE
*
* struct Resident *res = NULL;
*
* er_lock();
* while ( (res = er_nextresident(res)) ) {
* \* do something with this Resident tag *\
* }
* er_unlock();
*
* SEE ALSO
* er_findresident
*
******************************************************************************
*
*/
struct Resident * ASM er_nextresident(REG(a0,struct Resident *oldresident)) {
UWORD *wpt;
LONG numw;
if (glob_ss) {
if (oldresident) {
/* sanitycheck input */
if ( ((ULONG) oldresident < (ULONG) glob_ss->mh->mh_Lower) ||
(((ULONG) oldresident + sizeof(struct Resident)) >=
(ULONG) glob_ss->mh->mh_Upper) ) {
return NULL;
}
if ( (oldresident->rt_MatchWord == RTC_MATCHWORD) &&
(oldresident->rt_MatchTag == oldresident) ) {
wpt = (UWORD *) oldresident->rt_EndSkip;
} else {
wpt = (UWORD *) (((ULONG) oldresident + 3) & -2);
}
} else {
wpt = (UWORD *) glob_ss->mh->mh_Lower;
}
/* more sanity checking */
if ( ((ULONG) wpt < (ULONG) glob_ss->mh->mh_Lower) ||
(((ULONG) wpt + sizeof(struct Resident)) >=
(ULONG) glob_ss->mh->mh_Upper) ) {
return NULL;
}
numw = (((ULONG) glob_ss->mh->mh_Upper
- sizeof(struct Resident)
- (ULONG) wpt) ) >> 1;
if (numw > 0) {
for ( ; ; ) {
while (--numw && (*wpt++ != RTC_MATCHWORD));
if (!numw) return NULL;
if ( *(ULONG *) wpt == ((ULONG) wpt - 2) ) {
return (struct Resident *) ((ULONG) wpt - 2);
}
}
}
}
return NULL;
}
/****** bkapi.o/er_findresident **********************************************
*
* NAME
* er_findresident -- find resident tag from EXTRES buffer by name
*
* SYNOPSIS
* resident = er_findresident(name)
* D0 A1
*
* struct Resident *er_findresident(STRPTR);
*
* FUNCTION
* Scan EXTRES buffer memory for Resident tag with given name.
*
* WARNING
* You must have er_lock() on memory before calling this routine
* if you intend to access the resident tag found! Keep the lock
* until you're done accessing the Resident tag.
*
* INPUTS
* name - pointer to name string
*
* RESULT
* resident - pointer to the resident tag structure or
* zero if none found.
*
* EXAMPLE
*
* struct Resident *res = NULL;
*
* er_lock();
* if (res = er_findresident("EXTRES Handler")) {
* \* do something with the resident *\
*
* \* keep the lock until done! *\
* }
* er_unlock();
*
* NOTE
* exec.library/FindResident() will only find the currently *active*
* resident tags in EXTRES buffer memory. Also if resident tag with
* same name is found from both the ROM and EXTRES memory, the one
* having newer rt_Version will be activated on boot, and thus
* exec.library/FindResident() will find that particular resident.
*
* SEE ALSO
* er_nextresident, exec.library/FindResident, exec/resident.h
*
******************************************************************************
*
*/
struct Resident * ASM er_findresident(REG(a1,STRPTR name)) {
struct Resident *ret = NULL;
STRPTR s1, s2;
if (glob_ss) {
er_lock();
while ( (ret = er_nextresident(ret)) ) {
for ( s1 = name, s2 = ret->rt_Name; *s1 && (*s1 == *s2) ; s1++, s2++ );
if (*s1 == *s2) {
er_unlock();
return ret;
}
}
er_unlock();
}
return NULL;
}